import { TileLayer } from './TileLayer';
import { extend, setOptions, getParamString } from '../../core/Util';
import Browser from '../../core/Browser';
import { EPSG4326 } from '../../geo/crs/CRS.EPSG4326';
import { toBounds } from '../../geometry/Bounds';

/*
 * @class TileLayer.WMS
 * @inherits TileLayer
 * @aka L.TileLayer.WMS
 * Used to display [WMS](https://en.wikipedia.org/wiki/Web_Map_Service) services as tile layers on the map. Extends `TileLayer`.
 *
 * @example
 *
 * ```js
 * var nexrad = L.tileLayer.wms("http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi", {
 * 	layers: 'nexrad-n0r-900913',
 * 	format: 'image/png',
 * 	transparent: true,
 * 	attribution: "Weather data © 2012 IEM Nexrad"
 * });
 * ```
 */

export var TileLayerWMS = TileLayer.extend({
  // @section
  // @aka TileLayer.WMS options
  // If any custom options not documented here are used, they will be sent to the
  // WMS server as extra parameters in each request URL. This can be useful for
  // [non-standard vendor WMS parameters](https://docs.geoserver.org/stable/en/user/services/wms/vendor.html).
  defaultWmsParams: {
    service: 'WMS',
    request: 'GetMap',

    // @option layers: String = ''
    // **(required)** Comma-separated list of WMS layers to show.
    layers: '',

    // @option styles: String = ''
    // Comma-separated list of WMS styles.
    styles: '',

    // @option format: String = 'image/jpeg'
    // WMS image format (use `'image/png'` for layers with transparency).
    format: 'image/jpeg',

    // @option transparent: Boolean = false
    // If `true`, the WMS service will return images with transparency.
    transparent: false,

    // @option version: String = '1.1.1'
    // Version of the WMS service to use
    version: '1.1.1',
  },

  options: {
    // @option crs: CRS = null
    // Coordinate Reference System to use for the WMS requests, defaults to
    // map CRS. Don't change this if you're not sure what it means.
    crs: null,

    // @option uppercase: Boolean = false
    // If `true`, WMS request parameter keys will be uppercase.
    uppercase: false,
  },

  initialize: function (url, options) {
    this._url = url;

    var wmsParams = extend({}, this.defaultWmsParams);

    // all keys that are not TileLayer options go to WMS params
    for (var i in options) {
      if (!(i in this.options)) {
        wmsParams[i] = options[i];
      }
    }

    options = setOptions(this, options);

    var realRetina = options.detectRetina && Browser.retina ? 2 : 1;
    var tileSize = this.getTileSize();
    wmsParams.width = tileSize.x * realRetina;
    wmsParams.height = tileSize.y * realRetina;

    this.wmsParams = wmsParams;
  },

  onAdd: function (map) {
    this._crs = this.options.crs || map.options.crs;
    this._wmsVersion = parseFloat(this.wmsParams.version);

    var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs';
    this.wmsParams[projectionKey] = this._crs.code;

    TileLayer.prototype.onAdd.call(this, map);
  },

  getTileUrl: function (coords) {
    var tileBounds = this._tileCoordsToNwSe(coords),
      crs = this._crs,
      bounds = toBounds(crs.project(tileBounds[0]), crs.project(tileBounds[1])),
      min = bounds.min,
      max = bounds.max,
      bbox = (this._wmsVersion >= 1.3 && this._crs === EPSG4326
        ? [min.y, min.x, max.y, max.x]
        : [min.x, min.y, max.x, max.y]
      ).join(','),
      url = TileLayer.prototype.getTileUrl.call(this, coords);
    return (
      url +
      getParamString(this.wmsParams, url, this.options.uppercase) +
      (this.options.uppercase ? '&BBOX=' : '&bbox=') +
      bbox
    );
  },

  // @method setParams(params: Object, noRedraw?: Boolean): this
  // Merges an object with the new parameters and re-requests tiles on the current screen (unless `noRedraw` was set to true).
  setParams: function (params, noRedraw) {
    extend(this.wmsParams, params);

    if (!noRedraw) {
      this.redraw();
    }

    return this;
  },
});

// @factory L.tileLayer.wms(baseUrl: String, options: TileLayer.WMS options)
// Instantiates a WMS tile layer object given a base URL of the WMS service and a WMS parameters/options object.
export function tileLayerWMS(url, options) {
  return new TileLayerWMS(url, options);
}
